//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <winhack.h>
#include <ddk.h>
#include "CDeviceEventManager.h"
#define IncrementDllLockCount() do {} while(0)
#define DecrementDllLockCount() do {} while(0)
#include "_CDeviceEventManager.cpp"

ELAPI _CEvent_New(
        /* [in] */ Boolean manualReset,
        /* [in] */ EventState initialState,
        /* [out] */ IEvent **ppEvent);

ELAPI _CMutex_New(
        /* [out] */ IMutex **ppMutex);

ELAPI _CThread_New(
            PThreadMain pEntry,
            _ELASTOS PVoid pArg,
            ThreadCreationFlags flags,
            IThread **ppThread);

static PDeviceEventIsrBlockNode s_pIsrMemBlock = NULL;
static const Int32 c_nOrder = 1;
static DLinkNode g_imFreed;

static DLinkNode *s_pDeviceEventDispatcherList = NULL;
static DLinkNode s_asynchEventQueueHead;

static IMutex *s_pEveDelegateRW;
static IMutex *s_pAsyEveQueueMutex;

static IThread *s_pAsyncThread = NULL;
static IEvent *s_pIAsyncEvent;

static IDeviceEventDispatcher *GetIDeviceEventDispatcherFromEventNum(Int32 eventNum)
{
    DLinkNode *pHead = &s_pDeviceEventDispatcherList[EVENT_HASH_INDEX(eventNum)];
    PDeviceEventDispatcher ped = (PDeviceEventDispatcher)(pHead->Next());

    while (ped != (PDeviceEventDispatcher)pHead) {
        if (ped->uEventNum == eventNum) {
            return ped->pDeviceEventDispatcher;
        }
        ped = (PDeviceEventDispatcher)ped->Next();
    }

    return NULL;
}

Int32 AsynchEventHandler(void *pArg)
{
    PDeviceEventNodeBase penBase = NULL;
    IDeviceEventDispatcher *pEveDel = NULL;
    Boolean bIsrEvent;
    WaitResult wr;

    while (TRUE) {
        while (!s_asynchEventQueueHead.IsEmpty()) {
            penBase = (PDeviceEventNodeBase)(s_asynchEventQueueHead.First());
            penBase->Detach();
            bIsrEvent = penBase->bIsrParameter;

            s_pEveDelegateRW->Lock(&wr);
            pEveDel = GetIDeviceEventDispatcherFromEventNum(penBase->uEventNum);
            if (pEveDel) {
                if (!bIsrEvent) {
                    pEveDel->InvokeEventHandler(
                        *(((PDeviceEventNode)penBase)->pEventData));
                }
                else {
                    MemoryBuf arg((Byte *)(&(((PEventIsrNode)penBase)->isrpm)),
                        sizeof(EmIsrParameter));
                    pEveDel->InvokeEventHandler(arg);
                }
            }
            s_pEveDelegateRW->Unlock();

            if (!bIsrEvent) {
                PDeviceEventNode pen = (PDeviceEventNode)penBase;
                if (pen->pEventData) {
                    MemoryBuf::Free(pen->pEventData);
                }
                free(pen);
            }
            else {
                s_pAsyEveQueueMutex->Lock(&wr);
                PEventIsrNode pin = (PEventIsrNode)penBase;
                g_imFreed.InsertLast((DLinkNode *)&s_pIsrMemBlock[pin->uNum]);
                s_pAsyEveQueueMutex->Unlock();
            }

        }


        s_pIAsyncEvent->Wait(&wr, NULL);
    }

    return 0;
}

ECode CDeviceEventManager::SendEvent(
    /* [in] */ Int32 eventNum,
    /* [in] */ const MemoryBuf & eventData)
{
    WaitResult wr;
    s_pEveDelegateRW->Lock(&wr);

    IDeviceEventDispatcher *pEveDel = GetIDeviceEventDispatcherFromEventNum(eventNum);
    if (pEveDel) {
        pEveDel->InvokeEventHandler(eventData);
    }

    s_pEveDelegateRW->Unlock();

    return NOERROR;
}

ECode CDeviceEventManager::PostEvent(
    /* [in] */ Int32 eventNum,
    /* [in] */ const MemoryBuf & eventData)
{
    //save the event num
    PDeviceEventNode pDeviceEventNode = (PDeviceEventNode)malloc(sizeof(DeviceEventNode));
    if (NULL == pDeviceEventNode) return E_OUT_OF_MEMORY;
    memset(pDeviceEventNode, 0, sizeof(DeviceEventNode));
    pDeviceEventNode->uEventNum = eventNum;
    pDeviceEventNode->bIsrParameter = FALSE;

    //save the event data
    pDeviceEventNode->pEventData = NULL;
    if (!eventData.IsNullOrEmpty()) {
        Int32 nDataLen = eventData.GetUsed();
        pDeviceEventNode->pEventData = MemoryBuf::Alloc(nDataLen + 1);
        if (!pDeviceEventNode->pEventData) {
            delete pDeviceEventNode;
            return E_OUT_OF_MEMORY;
        }
        pDeviceEventNode->pEventData->Copy((MemoryBuf*)&eventData);
    }

    //put into the async event queue
    s_asynchEventQueueHead.InsertLast((DLinkNode*)((PDeviceEventNodeBase)pDeviceEventNode));

    s_pIAsyncEvent->Notify(SIGNALED);
    s_pIAsyncEvent->Clear();
    return NOERROR;
}

ECode CDeviceEventManager::AddEventHandler(
    /* [in] */ Int32 eventNum,
    /* [in] */ IDeviceEventDispatcher * pDelegate)
{
    if (NULL == pDelegate) return E_INVALID_ARGUMENT;

    PDeviceEventDispatcher ped = new DeviceEventDispatcher;
    if (NULL == ped) {
        return E_OUT_OF_MEMORY;
    }

    ped->pDeviceEventDispatcher = pDelegate;
    ped->pDeviceEventDispatcher->AddRef();

    ped->uEventNum = eventNum;

    //insert the event into the event pool
    WaitResult wr;
    s_pEveDelegateRW->Lock(&wr);
    s_pDeviceEventDispatcherList[EVENT_HASH_INDEX(eventNum)].InsertFirst(ped);
    s_pEveDelegateRW->Unlock();

    return NOERROR;
}

ECode CDeviceEventManager::RemoveEventHandler(
    /* [in] */ Int32 eventNum,
    /* [in] */ IDeviceEventDispatcher * pDelegate)
{
    WaitResult wr;
    s_pEveDelegateRW->Lock(&wr);
    DLinkNode *pHead = &s_pDeviceEventDispatcherList[EVENT_HASH_INDEX(eventNum)];
    PDeviceEventDispatcher ped = (PDeviceEventDispatcher)(pHead->Next());
    while (ped != (PDeviceEventDispatcher)pHead) {
        if (ped->uEventNum == eventNum) {
            ped->Detach();
            s_pEveDelegateRW->Unlock();
            if (ped->pDeviceEventDispatcher) {
                ped->pDeviceEventDispatcher->Release();
            }
            delete ped;
            return NOERROR;
        }
        ped = (PDeviceEventDispatcher)ped->Next();
    }
    s_pEveDelegateRW->Unlock();

    return NOERROR;
}

ECode CDeviceEventManager::Initialize()
{
    s_pDeviceEventDispatcherList = new DLinkNode[EVENT_HASH_LENGTH];
    if (NULL == s_pDeviceEventDispatcherList) {
        return E_OUT_OF_MEMORY;
    }

    UInt32 i;
    for (i = 0; i < EVENT_HASH_LENGTH; i++) {
        s_pDeviceEventDispatcherList[i].Initialize();
    }

    s_asynchEventQueueHead.Initialize();

    _CMutex_New(&s_pAsyEveQueueMutex);

    _CMutex_New(&s_pEveDelegateRW);

    _CEvent_New(TRUE, UNSIGNALED, &s_pIAsyncEvent);

    ECode ec = _CThread_New(AsynchEventHandler, (PVoid)this,
                        0, &s_pAsyncThread);
    if (FAILED(ec)) {
        delete s_pDeviceEventDispatcherList;
        return ec;
    }

    s_pIsrMemBlock = (PDeviceEventIsrBlockNode)DzAllocKernelPages(c_nOrder);
    if (NULL == s_pIsrMemBlock) {
        delete s_pDeviceEventDispatcherList;
        s_pAsyncThread->Release();
        return E_OUT_OF_MEMORY;
    }

    g_imFreed.Initialize();
    UInt32 mbNum = ((1u << c_nOrder) * PAGE_SIZE) / sizeof(DeviceEventIsrBlockNode);
    for (i = 0; i < mbNum; i++) {
        s_pIsrMemBlock[i].eventIsrNode.bIsrParameter = TRUE;
        s_pIsrMemBlock[i].eventIsrNode.uNum = i;
        g_imFreed.InsertLast((DLinkNode *)&s_pIsrMemBlock[i]);
    }

    return NOERROR;
}

CDeviceEventManager::~CDeviceEventManager()
{
    s_pAsyncThread->Release();

    PDeviceEventNodeBase pbn = NULL;
    while (!s_asynchEventQueueHead.IsEmpty()) {
        pbn = (PDeviceEventNodeBase)s_asynchEventQueueHead.First();
        pbn->Detach();
        if (!pbn->bIsrParameter) {
            delete ((PDeviceEventNode)pbn);
        }
    }

    UInt32 i;
    PDeviceEventDispatcher ped;
    for (i = 0; i < EVENT_HASH_LENGTH; i++) {
        while (!s_pDeviceEventDispatcherList[i].IsEmpty()) {
            ped = (PDeviceEventDispatcher)(s_pDeviceEventDispatcherList[i].First());
            ped->Detach();
            delete ped;
        }
    }

    delete s_pDeviceEventDispatcherList;

    DzFreeKernelPages(s_pIsrMemBlock, c_nOrder);
}

EXTERN ECode EmPostEventByIsr(
    /* [in] */ UInt32 uEventNum,
    /* [in] */ PEmIsrParameter pEmIsrParm)
{
    if (!g_imFreed.IsEmpty()) {
        PDeviceEventIsrBlockNode peb = (PDeviceEventIsrBlockNode)g_imFreed.First();
        peb->Detach();
        peb->eventIsrNode.uEventNum = uEventNum;
        memcpy((PVoid)&(peb->eventIsrNode.isrpm),
            pEmIsrParm, sizeof(EmIsrParameter));
        s_asynchEventQueueHead.InsertLast(
            (DLinkNode *)((PDeviceEventNodeBase)&(peb->eventIsrNode)));
    }

    s_pIAsyncEvent->Notify(SIGNALED);
    s_pIAsyncEvent->Clear();

    return NOERROR;
}
